package com.esdk.utils;

import com.alibaba.fastjson.JSONArray;
import com.esdk.esdk;
import com.esdk.sql.orm.ABRowSet;
import com.esdk.sql.orm.IRow;
/**
 * @author 范飞宇
 * @since 2023-1-15
 * 实现通用过滤器，支持黑白名单，优先级，过滤规则支持正则表达式匹配和包含匹配，并使用缓存提升匹配效率
 * */
public class EasyFilter{
  private static final String Priority="priority",IsBlock="isBlock", Rule="rule";
  private final ABRowSet<IRow> rs=new ABRowSet<IRow>(Priority,IsBlock,Rule);
  private LimitedHashMap<String,Boolean> limitedHashMap;
  private boolean isSorted=true,isDefaultAllow;

  public EasyFilter(){
    this(false,1000);
  }
  public EasyFilter(boolean defaultAllow){
    this(defaultAllow,1000);
  }
  public EasyFilter(boolean defaultAllow,int cacheLimitSize){
    isDefaultAllow=defaultAllow;
    limitedHashMap=new LimitedHashMap<String,Boolean>(cacheLimitSize);

  }
  /**
   * @param isBlock 黑名单
   * @param rules 过滤规则，支持多个，内容可以是url、key、正则表达式等
   * @param priority 优先级，越大优先级越高
   * */
  public EasyFilter add(int priority,boolean isBlock,String... rules){
    synchronized(rs){
      for(String rolePattern: rules){
        IRow row=rs.append();
        IRow existedRow=rs.filterFirst(false,IsBlock,isBlock,Rule,rolePattern);
        rs.remove(existedRow); //把相同的过滤规则先删除。
        row.put(IsBlock,isBlock)
          .put(Rule,wildcardToRegex(rolePattern))
          .put(Priority,priority);
      }
      isSorted=false;
    }
    return this;
  }

  public EasyFilter add(boolean isBlock,String... rules){
    return add(0,isBlock,rules);
  }

  public boolean isEmpty(){
    return rs.isEmpty();
  }

  public JSONArray exportSetting(){
    return rs.toJsonArray();
  }

  public ABRowSet getSetting(){
    return rs;
  }

  /**
   * 匹配是否通过，支持字符串比较、正则表达式、包括比较三种方式，并忽略大小写。
   * */
  public boolean allow(String content){
    if(!isSorted){
      synchronized(rs){
        rs.sort(!isDefaultAllow,IsBlock); //根据默认允许或禁止，决定相同优先级是允许或禁止。
        rs.sort(true,Priority,Rule); //优先级越高越靠前，规则越具体优先级越高。
        limitedHashMap.clear();
        isSorted=true;
      }
    }
    if(limitedHashMap.containsKey(content))
      return limitedHashMap.get(content);
    else{
      for(int i=0,n=rs.size();i<n;i++){
        IRow row=rs.getRow(i);
        String pattern=row.getString(Rule);
        if(content.contains(pattern)||pattern.equalsIgnoreCase(content)||content.matches(pattern)){ //忽略大小写
          boolean result=!row.getBoolean(IsBlock);
          limitedHashMap.put(content,result);
          return result;
        }
      }
    }
    limitedHashMap.put(content,isDefaultAllow);
    return isDefaultAllow;
  }

  /**星号变成正则表达式*/
  public static String wildcardToRegex(String rulePattern) {
    String regex=rulePattern.trim();
    if(rulePattern.contains("*")){
      regex=regex.replaceAll("\\.","\\\\.").replaceAll("\\*",".*");
    }
    return regex;
  }

  public void clearCache(){
    limitedHashMap.clear();
  }

  public void clear(){
    limitedHashMap.clear();
    rs.clear();
  }
}
